package map;

import java.awt.Point;
import java.util.ArrayList;
import java.util.List;

import building.Building;
import field.Field;
import field.Field.FieldType;

public class Map {

	private static final int NUMBER_OF_ROWS = 8;
	private static final int NUMBER_OF_COLUMNS = 9;
	/**
	 * Field[x][y]<br>
	 * x = column<br>
	 * y = row<br>
	 * <br>
	 * Example:<br>
	 * x=1<br>
	 * y=2<br>
	 * 0 0 0 0<br>
	 * 0 0 0 0<br>
	 * 0 X 0 0<br>
	 * 0 0 0 0<br>
	 */
	private Field[][] fields;
	private Field[] tiberiumFields;
	private Field[] crystalFields;
	private Field[] emptyFields;

	public Map(Point[] positionsOfTiberium, Point[] positionsOfCrystal) {
		fields = new Field[NUMBER_OF_COLUMNS][NUMBER_OF_ROWS];

		tiberiumFields = placeFieldsOFType(positionsOfTiberium, FieldType.TIBERIUM);
		crystalFields = placeFieldsOFType(positionsOfCrystal, FieldType.CRYSTAL);

		emptyFields = fillEmptyFields();
	}

	public Map(Map map) {
		fields = new Field[NUMBER_OF_COLUMNS][NUMBER_OF_ROWS];

		List<Field> emptyFields = new ArrayList<Field>();
		List<Field> tiberiumFields = new ArrayList<Field>();
		List<Field> crystalFields = new ArrayList<Field>();

		for (int y = 0; y < NUMBER_OF_ROWS; y++) {
			for (int x = 0; x < NUMBER_OF_COLUMNS; x++) {
				Field clonedField = map.fields[x][y].clone();
				fields[x][y] = clonedField;
				switch (clonedField.getFieldType()) {
				case CRYSTAL:
					crystalFields.add(clonedField);
					break;
				case EMPTY:
					emptyFields.add(clonedField);
					break;

				case TIBERIUM:
					tiberiumFields.add(clonedField);
					break;

				default:
					break;
				}
			}
		}

		this.tiberiumFields = tiberiumFields.toArray(new Field[0]);
		this.crystalFields = crystalFields.toArray(new Field[0]);
		this.emptyFields = emptyFields.toArray(new Field[0]);
	}

	private Field[] fillEmptyFields() {
		List<Field> placedFields = new ArrayList<Field>();
		for (int y = 0; y < NUMBER_OF_ROWS; y++) {
			for (int x = 0; x < NUMBER_OF_COLUMNS; x++) {
				if (fields[x][y] == null) {
					Field placedField = new Field(FieldType.EMPTY, new Point(x, y));
					fields[x][y] = placedField;
					placedFields.add(placedField);
				}
			}
		}
		return placedFields.toArray(new Field[0]);
	}

	private Field[] placeFieldsOFType(Point[] positionsOfFields, FieldType fieldType) {
		Field[] placedFields = new Field[positionsOfFields.length];
		for (int i = 0; i < positionsOfFields.length; i++) {
			Point pos = positionsOfFields[i];

			Field placedField = new Field(fieldType, pos);
			fields[pos.x][pos.y] = placedField;
			placedFields[i] = placedField;
		}
		return placedFields;
	}

	@Override
	public String toString() {
		StringBuffer sb = new StringBuffer();
		for (int y = 0; y < NUMBER_OF_ROWS; y++) {
			for (int x = 0; x < NUMBER_OF_COLUMNS; x++) {
				sb.append("[");
				sb.append(fields[x][y]);
				sb.append("]");
			}
			sb.replace(sb.length() - 1, sb.length(), "\n");
		}

		return sb.toString();
	}

	public double getOverallValue() {
		double value = 0;
		for (int y = 0; y < NUMBER_OF_ROWS; y++) {
			for (int x = 0; x < NUMBER_OF_COLUMNS; x++) {
				if (fields[x][y].isCovered()) {
					Field[] neighborFields = getNeighborsOf(x, y);
					fields[x][y].setNeighborFields(neighborFields);
					value = value + fields[x][y].calcValue();
				}
			}
		}
		return value;
	}

	private Field[] getNeighborsOf(int x, int y) {
		List<Field> neighbors = new ArrayList<Field>();
		if (x > 0) {
			if (y > 0) {
				neighbors.add(fields[x - 1][y - 1]);
			}
			if (y < NUMBER_OF_ROWS - 1) {
				neighbors.add(fields[x - 1][y + 1]);
			}
			neighbors.add(fields[x - 1][y]);
		}
		if (x < NUMBER_OF_COLUMNS - 1) {
			if (y > 0) {
				neighbors.add(fields[x + 1][y - 1]);
			}
			if (y < NUMBER_OF_ROWS - 1) {
				neighbors.add(fields[x + 1][y + 1]);
			}
			neighbors.add(fields[x + 1][y]);
		}
		if (y > 0) {
			neighbors.add(fields[x][y - 1]);
		}
		if (y < NUMBER_OF_ROWS - 1) {
			neighbors.add(fields[x][y + 1]);
		}

		return neighbors.toArray(new Field[0]);
	}

	public List<Map> createAllVariations(Building[] buildings) {
		List<Map> maps = new ArrayList<Map>();

		boolean hasVariations = true;

		while (hasVariations) {
			Map mapVariation = new Map(this);
			for (int i = 0; i < buildings.length; i++) {
				//TODO: a lot :(
			}
			maps.add(mapVariation);
		}

		return maps;
	}

}
